﻿module TetrisLogic

open Utilities
open Variable
open Coroutines
open TetrisState

open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Input

let Raws = 15
let Columns = 11

let numPositions = Raws*Columns

let init_vel = 40.0f<pixel/s>

let vel_factor = 4.0f<pixel/s>

let PieceWH = 32.0f

let first_x = 137.0f

let frameWait = 7

let tetris_state = 
  {
    CurrentBlock = Variable(fun () -> None)
    NextBlock = Variable(fun () -> None)
    BlockMatrix = Variable(fun () -> [])
    PlacedBricks = Variable(fun () -> [])
    Score = variable(0)
    Level = variable(0)
  }

let init_block_matrix () =
  tetris_state.BlockMatrix := 
    [
      for i = 0 to numPositions-1 do
        if i < Columns then
         yield 1<id>
        else
          yield 0<id>
    ]

let mutable CompleteRaws = []

let raws_check () =
  let mutable isRaw = false
  for i = 1 to Raws-1 do
    let mutable count = 0
    for a = 0 to Columns-1 do
      if (!tetris_state.BlockMatrix).[(i*Columns)+a] = 1<id> then
        count <- count + 1
    if count = Columns then
      CompleteRaws <- (i :: CompleteRaws)
      isRaw <- true
  isRaw

let remove_placed_bricks () =
  let c = variable(true)
  tetris_state.PlacedBricks := 
    [
      for br = 0 to (!tetris_state.PlacedBricks).Length-1 do
        c := true
        for raw in CompleteRaws do  
          if (!(!tetris_state.PlacedBricks).[br].Indexes).[1] = (raw* 1<ij>) then
             c := false

        if (immediate_read c) = true then
          yield (!tetris_state.PlacedBricks).[br]
    ]

let update_placed_bricks () =
  let up = variable(0)
  tetris_state.PlacedBricks := 
    [
      for br = 0 to (!tetris_state.PlacedBricks).Length-1 do
        for raw in CompleteRaws do
          if (!(!tetris_state.PlacedBricks).[br].Indexes).[1] > (raw* 1<ij>) then
            up := (immediate_read up) + 1
        
        if (immediate_read up) > 0 then
          (!tetris_state.PlacedBricks).[br].Indexes := [ (!(!tetris_state.PlacedBricks).[br].Indexes).[0]; (!(!tetris_state.PlacedBricks).[br].Indexes).[1]-((immediate_read up)* 1<ij>) ]
          up := 0
        yield (!tetris_state.PlacedBricks).[br]
    ]

let set_placed_xy () =
  for bk in !tetris_state.PlacedBricks do
    bk.X := ((((!bk.Indexes).[0] |> float32)*PieceWH)+first_x)* 1.0f<pixel>
    bk.Y := ((((Raws |> float32)-((!bk.Indexes).[1] |> float32))*PieceWH)+(600.0f/10.0f))* 1.0f<pixel>

let remove_raw () =
  if (!tetris_state.CurrentBlock).IsSome = true then
    let b = variable(true)
    tetris_state.BlockMatrix := 
      [
        for i = 0 to Raws-1 do
          b := true
          for raw in CompleteRaws do
            if i = raw && (immediate_read b) = true then
              b := false
          if (immediate_read b) = true then
            for a = 0 to Columns-1 do
              yield (!tetris_state.BlockMatrix).[(i*Columns)+a]
      ] @
      [
        for raw in CompleteRaws do
          for a = 0 to Columns-1 do
            yield 0<id>
      ]

let collision_check () = 
  let mutable isCollision = false
  if (!tetris_state.CurrentBlock).IsSome = true then
    for br in (!tetris_state.CurrentBlock).Value.Bricks do
      let mutable indx = 0<id>
      let mutable indx1 = 0<id>
      indx <- (!tetris_state.BlockMatrix).[((((!br.Indexes).[1]-1<ij>)*Columns)+(!br.Indexes).[0])/1<ij>]
      indx1 <- (!tetris_state.BlockMatrix).[((((!br.Indexes).[1])*Columns)+(!br.Indexes).[0])/1<ij>]
      if indx = 1<id> || indx1 = 1<id> then
        isCollision <- true
      else
        isCollision <- isCollision
  isCollision

let new_ipiece (vel:float32<pixel/s>) =
  {
    Name = BlockName.IPiece
    VelY = variable(vel)
    Orientation = variable(0)
    OrientationRange = 2
    Bricks = [
              {
                Name = BlockName.IPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.IPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.IPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-2)* 1<ij> ])
              }
              {
                Name = BlockName.IPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-3)* 1<ij> ])
              }
             ]
  }

let new_jpiece (vel:float32<pixel/s>) =
  {
    Name = BlockName.JPiece
    VelY = variable(vel)
    Orientation = variable(0)
    OrientationRange = 4
    Bricks = [
              {
                Name = BlockName.JPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.JPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.JPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-2)* 1<ij> ])
              }
              {
                Name = BlockName.JPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)-1)* 1<ij>; ((Raws-1)-2)* 1<ij> ])
              }
             ]
  }

let new_lpiece (vel:float32<pixel/s>) =
  {
    Name = BlockName.LPiece
    VelY = variable(vel)
    Orientation = variable(0)
    OrientationRange = 4
    Bricks = [
              {
                Name = BlockName.LPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.LPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.LPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-2)* 1<ij> ])
              }
              {
                Name = BlockName.LPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)+1)* 1<ij>; ((Raws-1)-2)* 1<ij> ])
              }
             ]
  }

let new_opiece (vel:float32<pixel/s>) =
  {
    Name = BlockName.OPiece
    VelY = variable(vel)
    Orientation = variable(0)
    OrientationRange = 1
    Bricks = [
              {
                Name = BlockName.OPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.OPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.OPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.OPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
             ]
  }

let new_spiece (vel:float32<pixel/s>) =
  {
    Name = BlockName.SPiece
    VelY = variable(vel)
    Orientation = variable(0)
    OrientationRange = 2
    Bricks = [
              {
                Name = BlockName.SPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.SPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.SPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.SPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)-1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
             ]
  }

let new_tpiece (vel:float32<pixel/s>) =
  {
    Name = BlockName.TPiece
    VelY = variable(vel)
    Orientation = variable(0)
    OrientationRange = 4
    Bricks = [
              {
                Name = BlockName.TPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.TPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.TPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.TPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)-1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
             ]
  }

let new_zpiece (vel:float32<pixel/s>) =
  {
    Name = BlockName.ZPiece
    VelY = variable(vel)
    Orientation = variable(0)
    OrientationRange = 2
    Bricks = [
              {
                Name = BlockName.ZPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.ZPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)-1)* 1<ij>; (Raws-1)* 1<ij> ])
              }
              {
                Name = BlockName.ZPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ (((Columns-1)/2)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
              {
                Name = BlockName.ZPiece
                X = variable(50.0f<pixel>)
                Y = variable(50.0f<pixel>)
                ID = 2
                Indexes = Variable(fun () -> [ ((((Columns-1)/2)+1)+1)* 1<ij>; ((Raws-1)-1)* 1<ij> ])
              }
             ]
  }

let set_next_xy () =
  if (!tetris_state.NextBlock).IsSome = true then
    for bk in (!tetris_state.NextBlock).Value.Bricks do
      bk.X := ((((!bk.Indexes).[0] |> float32)*PieceWH)+first_x)* 1.0f<pixel>
      bk.Y := ((((Raws |> float32)-((!bk.Indexes).[1] |> float32))*PieceWH)+(600.0f/10.0f))* 1.0f<pixel>

let spawn_block (vel:float32<pixel/s>) =
  tetris_state.CurrentBlock := !tetris_state.NextBlock
  let block = random.Next(1, 12)
  match block with
  | 1 -> tetris_state.NextBlock := Some(new_ipiece (vel))
  | 2 -> tetris_state.NextBlock := Some(new_jpiece (vel))
  | 3 -> tetris_state.NextBlock := Some(new_lpiece (vel))
  | 4 -> tetris_state.NextBlock := Some(new_jpiece (vel))
  | 5 -> tetris_state.NextBlock := Some(new_lpiece (vel))
  | 6 -> tetris_state.NextBlock := Some(new_opiece (vel))
  | 7 -> tetris_state.NextBlock := Some(new_spiece (vel))
  | 8 -> tetris_state.NextBlock := Some(new_tpiece (vel))
  | 9 -> tetris_state.NextBlock := Some(new_zpiece (vel))
  | 10 -> tetris_state.NextBlock := Some(new_spiece (vel))
  | 11 -> tetris_state.NextBlock := Some(new_tpiece (vel))
  | 12 -> tetris_state.NextBlock := Some(new_zpiece (vel))

let update_matrix (isCollision:bool) = 
  if (!tetris_state.CurrentBlock).IsSome = true then
    let b = variable(true)
    tetris_state.BlockMatrix := 
      [
        for i = 0 to Raws-1 do
          for a = 0 to Columns-1 do
            b := true
            for br in (!tetris_state.CurrentBlock).Value.Bricks do
              if (immediate_read b) = true then
                if (!br.Indexes).[0] = a* 1<ij> 
                  && 
                  (!br.Indexes).[1] = i* 1<ij> then
                  if isCollision = true then
                    yield 1<id>
                    b := false
                  else
                    yield 2<id>
                    b := false
            if (immediate_read b) = true then
              if (!tetris_state.BlockMatrix).[(i*Columns)+a] = 1<id> then
                yield 1<id>
              else
                yield 0<id>
      ]

let print_matrix () =
  let mutable stmp = 0
  for i = Raws-1 downto 0 do
    printf "\n"
    for a = 0 to Columns-1 do
      stmp <- ((!tetris_state.BlockMatrix).[(i*Columns)+a]/ 1<id>)
      printf "%d " stmp
  printf "\n"

let mutable right_press = 0
let mutable left_press = 0

let mutable KeyOldState = Keyboard.GetState()

let move_piece () = 
  let mutable bool_move = true
  if (!tetris_state.CurrentBlock).IsSome = true then
    if Keyboard.GetState().[Keys.Right] = KeyState.Down
      &&
      KeyOldState.[Keys.Right] = KeyState.Up then
      for br in (!tetris_state.CurrentBlock).Value.Bricks do
        if ((!br.Indexes).[0] >= (Columns-1)* 1<ij>) || ((!tetris_state.BlockMatrix).[(((!br.Indexes).[1]*Columns)+(!br.Indexes).[0]+1<ij>)/1<ij>] = 1<id>) then
          bool_move <- false
      if bool_move = true then
        for br in (!tetris_state.CurrentBlock).Value.Bricks do
          br.Indexes := [ ((!br.Indexes).[0])+1<ij>; (!br.Indexes).[1] ]
      bool_move <- true
      right_press <- 0

    else if Keyboard.GetState().[Keys.Right] = KeyState.Down
      &&
      KeyOldState.[Keys.Right] = KeyState.Down then
      right_press <- right_press + 1
      if collision_check () = true then
        right_press <- frameWait

      if right_press = frameWait then
        for br in (!tetris_state.CurrentBlock).Value.Bricks do
          if ((!br.Indexes).[0] >= (Columns-1)* 1<ij>) || ((!tetris_state.BlockMatrix).[(((!br.Indexes).[1]*Columns)+(!br.Indexes).[0]+1<ij>)/1<ij>] = 1<id>) then
            bool_move <- false
        if bool_move = true then
          for br in (!tetris_state.CurrentBlock).Value.Bricks do
            br.Indexes := [ ((!br.Indexes).[0])+1<ij>; (!br.Indexes).[1] ]
        bool_move <- true
        right_press <- 0

    else if Keyboard.GetState().[Keys.Left] = KeyState.Down
      &&
      KeyOldState.[Keys.Left] = KeyState.Up then
      for br in (!tetris_state.CurrentBlock).Value.Bricks do
        if ((!br.Indexes).[0] <= 0<ij>) || ((!tetris_state.BlockMatrix).[(((!br.Indexes).[1]*Columns)+(!br.Indexes).[0]-1<ij>)/1<ij>] = 1<id>) then
          bool_move <- false
      if bool_move = true then
        for br in (!tetris_state.CurrentBlock).Value.Bricks do
          br.Indexes := [ ((!br.Indexes).[0])-1<ij>; (!br.Indexes).[1] ]
      bool_move <- true
      left_press <- 0

    else if Keyboard.GetState().[Keys.Left] = KeyState.Down
      &&
      KeyOldState.[Keys.Left] = KeyState.Down then
      left_press <- left_press + 1
      if collision_check () = true then
        left_press <- frameWait

      if left_press = frameWait then
        for br in (!tetris_state.CurrentBlock).Value.Bricks do
          if ((!br.Indexes).[0] <= 0<ij>) || ((!tetris_state.BlockMatrix).[(((!br.Indexes).[1]*Columns)+(!br.Indexes).[0]-1<ij>)/1<ij>] = 1<id>) then
            bool_move <- false
        if bool_move = true then
          for br in (!tetris_state.CurrentBlock).Value.Bricks do
            br.Indexes := [ ((!br.Indexes).[0])-1<ij>; (!br.Indexes).[1] ]
        bool_move <- true
        left_press <- 0

    if Keyboard.GetState().[Keys.Down] = KeyState.Down then
      (!tetris_state.CurrentBlock).Value.VelY := 5.0f<pixel/s>
    else
      (!tetris_state.CurrentBlock).Value.VelY := (init_vel - ((tetris_state.GetLevel() |> float32)*vel_factor))

let rotate_ibricks () =
  let pivot = (!(!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes)
  if (!(!tetris_state.CurrentBlock).Value.Orientation) = 1 then
    if pivot.[0] > 0<ij> && pivot.[0] < (Columns-2)* 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]+2<ij>; pivot.[1] ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 0

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 0 then
    if pivot.[1] > 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]; pivot.[1]-2<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 1

let rotate_jbricks () =
  let pivot = (!(!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes)
  if (!(!tetris_state.CurrentBlock).Value.Orientation) = 1 then
    if pivot.[0] > 0<ij> && pivot.[0] < (Columns-1)* 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]-1<ij>; pivot.[1]+1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 0

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 2 then
    if pivot.[1] < (Raws-1)* 1<ij> && pivot.[1] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]+1<ij>; pivot.[1]+1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 1

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 3 then
    if pivot.[1] > 0<ij> && pivot.[0] > 0<ij> && pivot.[0] < (Columns-1)* 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]+1<ij>; pivot.[1]-1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 2

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 0 then
    if pivot.[1] > 0<ij> && pivot.[0] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]-1<ij>; pivot.[1]-1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 3

let rotate_lbricks () =
  let pivot = (!(!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes)
  if (!(!tetris_state.CurrentBlock).Value.Orientation) = 1 then
    if pivot.[0] > 0<ij> && pivot.[0] < (Columns-1)* 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]-1<ij>; pivot.[1]-1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 0

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 2 then
    if pivot.[1] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]-1<ij>; pivot.[1]+1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 1

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 3 then
    if pivot.[0] > 0<ij> && pivot.[0] < (Columns-1)* 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]+1<ij>; pivot.[1]+1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 2

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 0 then
    if pivot.[1] > 0<ij> && pivot.[0] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]+1<ij>; pivot.[1]-1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 3

let rotate_sbricks () =
  let pivot = (!(!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes)
  if (!(!tetris_state.CurrentBlock).Value.Orientation) = 1 then
    if pivot.[1] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := [ pivot.[0]-1<ij>; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 0

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 0 then
    if pivot.[0] < (Columns-1)* 1<ij> && pivot.[0] > 0<ij>then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := [ pivot.[0]+1<ij>; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 1

let rotate_tbricks () =
  let pivot = (!(!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes)
  if (!(!tetris_state.CurrentBlock).Value.Orientation) = 1 then
    if pivot.[1] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 0

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 2 then
    if pivot.[0] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 1

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 3 then
    if pivot.[0] < (Columns-1)* 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 2

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 0 then
    if pivot.[0] < (Columns-1)* 1<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]-1<ij>; pivot.[1] ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 3

let rotate_zbricks () =
  let pivot = (!(!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes)
  if (!(!tetris_state.CurrentBlock).Value.Orientation) = 1 then
    if pivot.[1] > 0<ij> then
      (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]
      (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := [ pivot.[0]+1<ij>; pivot.[1]+1<ij> ]
      (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := pivot
      (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]; pivot.[1]-1<ij> ]
    else
      (!tetris_state.CurrentBlock).Value.Orientation := 0

  else if (!(!tetris_state.CurrentBlock).Value.Orientation) = 0 then
    (!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := [ pivot.[0]; pivot.[1]+1<ij> ]
    (!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := [ pivot.[0]-1<ij>; pivot.[1]+1<ij> ]
    (!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := pivot
    (!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := [ pivot.[0]+1<ij>; pivot.[1] ]

let rotate_bricks () = 
  match (!tetris_state.CurrentBlock).Value.Name with
  | BlockName.IPiece -> rotate_ibricks ()
  | BlockName.JPiece -> rotate_jbricks ()
  | BlockName.LPiece -> rotate_lbricks ()
  | BlockName.OPiece -> ()
  | BlockName.SPiece -> rotate_sbricks ()
  | BlockName.TPiece -> rotate_tbricks ()
  | BlockName.ZPiece -> rotate_zbricks ()

let update_variable () =
  if (!tetris_state.CurrentBlock).IsSome = true then
    if (!tetris_state.CurrentBlock).Value.Bricks.IsEmpty = false then
      for br in (!tetris_state.CurrentBlock).Value.Bricks do
        br.Indexes := (!br.Indexes)
        br.X := !br.X
        br.Y := !br.Y

  if (!tetris_state.NextBlock).IsSome = true then
    if (!tetris_state.NextBlock).Value.Bricks.IsEmpty = false then
      for br in (!tetris_state.NextBlock).Value.Bricks do
        br.Indexes := (!br.Indexes)
        br.X := !br.X
        br.Y := !br.Y
  
  for br  in !tetris_state.PlacedBricks do
    br.Indexes := (!br.Indexes)
    br.X := !br.X
    br.Y := !br.Y

  if (!tetris_state.CurrentBlock).IsSome = true then
    (!tetris_state.CurrentBlock).Value.VelY := (!(!tetris_state.CurrentBlock).Value.VelY)
    (!tetris_state.CurrentBlock).Value.Orientation := (!(!tetris_state.CurrentBlock).Value.Orientation)

  if (!tetris_state.NextBlock).IsSome = true then
    (!tetris_state.NextBlock).Value.VelY := (!(!tetris_state.NextBlock).Value.VelY)
    (!tetris_state.NextBlock).Value.Orientation := (!(!tetris_state.NextBlock).Value.Orientation)

  tetris_state.CurrentBlock := (!tetris_state.CurrentBlock)
  tetris_state.NextBlock := (!tetris_state.NextBlock)
  tetris_state.BlockMatrix := (!tetris_state.BlockMatrix)
  tetris_state.PlacedBricks := (!tetris_state.PlacedBricks)
  tetris_state.Score := (!tetris_state.Score)
  tetris_state.Level := (!tetris_state.Level)

let destroy_game () =
  if (!tetris_state.CurrentBlock).IsSome = true then
    if (!tetris_state.CurrentBlock).Value.Bricks.IsEmpty = false then
      for br in (!tetris_state.CurrentBlock).Value.Bricks do
        br.Indexes := []
        br.X := 0.0f<pixel>
        br.Y := 0.0f<pixel>

  if (!tetris_state.NextBlock).IsSome = true then
    if (!tetris_state.NextBlock).Value.Bricks.IsEmpty = false then
      for br in (!tetris_state.NextBlock).Value.Bricks do
        br.Indexes := []
        br.X := 0.0f<pixel>
        br.Y := 0.0f<pixel>

  for br  in !tetris_state.PlacedBricks do
    br.Indexes := []
    br.X := 0.0f<pixel>
    br.Y := 0.0f<pixel>

  if (!tetris_state.CurrentBlock).IsSome = true then
    (!tetris_state.CurrentBlock).Value.VelY := 0.0f<pixel/s>
    (!tetris_state.CurrentBlock).Value.Orientation := 0

  if (!tetris_state.NextBlock).IsSome = true then
    (!tetris_state.NextBlock).Value.VelY := 0.0f<pixel/s>
    (!tetris_state.NextBlock).Value.Orientation := 0

  tetris_state.CurrentBlock := None
  tetris_state.NextBlock := None
  tetris_state.BlockMatrix := []
  tetris_state.PlacedBricks := []
  tetris_state.Score := 0
  tetris_state.Level := 0

  commit_variable_updates ()
  update_variable ()

let rotate_piece () =
  if (!tetris_state.CurrentBlock).IsSome = true then
    if Keyboard.GetState().[Keys.Up] = KeyState.Down
      &&
      KeyOldState.[Keys.Up] = KeyState.Up then
      let tmp_orientation = !(!tetris_state.CurrentBlock).Value.Orientation
      let tmp_br0_indexes = !(!tetris_state.CurrentBlock).Value.Bricks.[0].Indexes
      let tmp_br1_indexes = !(!tetris_state.CurrentBlock).Value.Bricks.[1].Indexes
      let tmp_br2_indexes = !(!tetris_state.CurrentBlock).Value.Bricks.[2].Indexes
      let tmp_br3_indexes = !(!tetris_state.CurrentBlock).Value.Bricks.[3].Indexes
      (!tetris_state.CurrentBlock).Value.Orientation := (((!(!tetris_state.CurrentBlock).Value.Orientation)+1)%(!tetris_state.CurrentBlock).Value.OrientationRange)
      commit_variable_updates ()
      update_variable ()
      rotate_bricks ()
      let mutable b = true
      for br in (immediate_read tetris_state.CurrentBlock).Value.Bricks do
        if b = true && (immediate_read tetris_state.BlockMatrix).[(((immediate_read br.Indexes).[1]*Columns)+(immediate_read br.Indexes).[0])/1<ij>] = 1<id> then
          (immediate_read tetris_state.CurrentBlock).Value.Bricks.[0].Indexes := tmp_br0_indexes
          (immediate_read tetris_state.CurrentBlock).Value.Bricks.[1].Indexes := tmp_br1_indexes
          (immediate_read tetris_state.CurrentBlock).Value.Bricks.[2].Indexes := tmp_br2_indexes
          (immediate_read tetris_state.CurrentBlock).Value.Bricks.[3].Indexes := tmp_br3_indexes
          (immediate_read tetris_state.CurrentBlock).Value.Orientation := tmp_orientation
          b <- false

let update_keyboard () = KeyOldState <- Keyboard.GetState()

let scroll_block () =
  let mutable bool_scroll = true
  if (!tetris_state.CurrentBlock).IsSome = true then
    for br in (!tetris_state.CurrentBlock).Value.Bricks do
      if (!tetris_state.BlockMatrix).[((((!br.Indexes).[1]-1<ij>)*Columns)+(!br.Indexes).[0])/1<ij>] = 1<id> then
          bool_scroll <- false

    if bool_scroll = true then
      for br in (!tetris_state.CurrentBlock).Value.Bricks do
        br.Indexes := [ (!br.Indexes).[0]; (!br.Indexes).[1]-1<ij> ]

let set_velocity () =
  (!tetris_state.CurrentBlock).Value.VelY := (init_vel - ((tetris_state.GetLevel() |> float32)*vel_factor))
  
let update_raws (isRaw:bool) = 
  if isRaw = true then
    for r in CompleteRaws do
      tetris_state.BlockMatrix := 
      [
        for i = 0 to Raws-1 do
          if i <> r then
            for a = 0 to Columns-1 do
              yield (!tetris_state.BlockMatrix).[(i*Raws)+a]
      ]
      tetris_state.BlockMatrix := 
      (!tetris_state.BlockMatrix) @ 
      [
        for a = 0 to Columns-1 do
          yield 0<id>
      ]

let set_xy () =
  if (!tetris_state.CurrentBlock).IsSome = true then
    for bk in (!tetris_state.CurrentBlock).Value.Bricks do
    //(800.0f/3.0f)
      bk.X := ((((!bk.Indexes).[0] |> float32)*PieceWH)+first_x)* 1.0f<pixel>
      bk.Y := ((((Raws |> float32)-((!bk.Indexes).[1] |> float32))*PieceWH)+(600.0f/10.0f))* 1.0f<pixel>

let init_current_block (vel:float32<pixel/s>) =
  let block = random.Next(1, 7)
  match block with
  | 1 -> tetris_state.CurrentBlock := Some(new_ipiece (vel))
  | 2 -> tetris_state.CurrentBlock := Some(new_jpiece (vel))
  | 3 -> tetris_state.CurrentBlock := Some(new_lpiece (vel))
  | 4 -> tetris_state.CurrentBlock := Some(new_opiece (vel))
  | 5 -> tetris_state.CurrentBlock := Some(new_spiece (vel))
  | 6 -> tetris_state.CurrentBlock := Some(new_tpiece (vel))
  | 7 -> tetris_state.CurrentBlock := Some(new_zpiece (vel))

let init_next_block (vel:float32<pixel/s>) = 
  let block = random.Next(1, 7)
  match block with
  | 1 -> tetris_state.NextBlock := Some(new_ipiece (vel))
  | 2 -> tetris_state.NextBlock := Some(new_jpiece (vel))
  | 3 -> tetris_state.NextBlock := Some(new_lpiece (vel))
  | 4 -> tetris_state.NextBlock := Some(new_opiece (vel))
  | 5 -> tetris_state.NextBlock := Some(new_spiece (vel))
  | 6 -> tetris_state.NextBlock := Some(new_tpiece (vel))
  | 7 -> tetris_state.NextBlock := Some(new_zpiece (vel))

let add_placed_bricks () =
  tetris_state.PlacedBricks := 
    (!tetris_state.PlacedBricks) @

    [
      for br in (!tetris_state.CurrentBlock).Value.Bricks do
        yield br
    ]

let init_game () =
  init_current_block(init_vel - ((tetris_state.GetLevel() |> float32)*vel_factor))
  init_next_block(init_vel - ((tetris_state.GetLevel() |> float32)*vel_factor))
  commit_variable_updates()
  update_variable()
  set_xy()
  set_next_xy()
  commit_variable_updates()
  update_variable()

let mutable score = 0

let update_score_level () =
  for raw in CompleteRaws do
    tetris_state.Score := (immediate_read tetris_state.Score) + 10
    score <- score + 10
  if score >= 50 then
    tetris_state.Level := (!tetris_state.Level)+(score/50)
    score <- score - 50

let game_over () =
  let mutable isOver = false
  for a = 0 to Columns-1 do
    if (!tetris_state.BlockMatrix).[((Raws-1)*Columns)+a] = 1<id> then
      isOver <- true
  isOver

let mutable ScrollCheck = 0.0f<pixel/s>

let scrolling_script () =
  let scroll =
    co{
        do! yield_
        
        if ScrollCheck >= tetris_state.GetVelY() then
          scroll_block()
          commit_variable_updates()
          update_variable()
          update_matrix(false)
          commit_variable_updates()
          update_variable()          
          ScrollCheck <- 0.0f<pixel/s>
        else
          ScrollCheck <- ScrollCheck + 1.0f<pixel/s>
        
        return ()
      }

  repeat_ scroll 
  
let moving_script () =
  let move = 
    co{
        do! yield_
        
        move_piece()
        commit_variable_updates()
        update_variable()
        rotate_piece()
        update_keyboard()
        commit_variable_updates()
        update_variable()
        set_xy()
        commit_variable_updates()
        update_variable()
        update_matrix(false)
        commit_variable_updates()
        update_variable()

        if collision_check () = true then
          update_matrix(true)
          commit_variable_updates()
          update_variable()
          add_placed_bricks()
          spawn_block(init_vel - ((tetris_state.GetLevel() |> float32)*vel_factor))
          commit_variable_updates()
          update_variable()
          set_next_xy()
          commit_variable_updates()
          update_variable()
        
        return ()
      }

  repeat_ move

//let collision_script () =
//  let collision_check_script =
//    co{
//        do! yield_
//        return collision_check ()
//      }
//
//  let collision = 
//    co{
//        do! yield_
//
//        update_matrix(true)
//        commit_variable_updates()
//        update_variable()
//        add_placed_bricks()
//        spown_block(init_vel - ((tetris_state.GetLevel() |> float32)*vel_factor))
//        commit_variable_updates()
//        update_variable()
//        set_next_xy()
//        commit_variable_updates()
//        update_variable()
//        print_matrix()
//
//        return ()
//      }
//
//  repeat_ (collision_check_script => collision)

let raws_deletion_script () =
  let raw_check_script =
    co{
        do! yield_
        return raws_check ()
      }

  let delete =
    co{
        do! yield_
        
        remove_raw ()
        commit_variable_updates()
        update_variable()
        remove_placed_bricks ()
        commit_variable_updates()
        update_variable()
        update_placed_bricks ()
        commit_variable_updates()
        update_variable()
        set_placed_xy ()
        update_score_level ()
        commit_variable_updates()
        update_variable()
        CompleteRaws <- []
      
        return ()
      }

  repeat_ (raw_check_script => delete)

let mutable co_scroll =
  scrolling_script ()

let mutable co_move =
  moving_script ()

//let mutable co_collision =
//  collision_script ()

let mutable co_delete =
  raws_deletion_script ()

let update_script () =
  if game_over () = false then
    co_scroll <- update_ai (co_scroll)
    co_move <- update_ai (co_move)
    //co_collision <- update_ai (co_collision)
    co_delete <- update_ai (co_delete)
  else
    co_scroll <- co{return ()}
    co_move <- co{return ()}
    //co_collision <- co{return ()}
    co_delete <- co{return ()}
  
  game_over ()